﻿using System.Collections.Generic;
using System.Linq;
using System.Text;
using eAtlantida.Tools.Generator.Sql;

namespace eAtlantida.Tools.Generator
{
    public class ClientGenerator
    {
        private readonly bool _isCreate;

        private readonly bool _isDelete;

        private readonly bool _isListing;

        private readonly bool _isSave;

        private readonly string prefix;

        private readonly string name;

        private string singleName
        {
            get
            {
                if (name.EndsWith("es"))
                {
                    return name.Substring(0, name.Length - 2);
                }
                return name.Substring(0, name.Length - 1);
            }
        }

        private readonly IEnumerable<ColumnInformation> _columns;

        public ClientGenerator(IEnumerable<ColumnInformation> columns, string name, string prefix = "Admin", bool isCreate= true, bool isSave = true, bool isDelete = true, bool isListing = true)
        {
            _isCreate = isCreate;
            _isDelete = isDelete;
            _isListing = isListing;
            _isSave = isSave;
            _columns = columns;
            this.name = name;
            this.prefix = prefix;
        }

        public string GenerateListingModel()
        {
            var result = new StringBuilder();

            result.AppendLine("class @", prefix, singleName, "ListingModel extends ListingModel");
            result.AppendLine(1, "@ListingUrl:''");
            result.AppendLine(1, "initialize: ->");
            result.AppendLine(2, "if Admin", singleName, "ListingModel.ListingUrl.length is 0");
            result.AppendLine(3, "Admin", singleName, "ListingModel.ListingUrl = SiteMap.Admin.", name, ".Listing");
            result.AppendLine();
            result.AppendLine(1, "defaults:");
            result.AppendLine(2, "Size: 10");
            result.AppendLine(2, "Page: 1");
            result.AppendLine(2, "Direction: 0");
            result.AppendLine(2, name, ": []");
            var firstNotIdentityField = _columns.FirstOrDefault(x => !x.IsIdentity);
            if (firstNotIdentityField != null)
            {
                result.AppendLine(2, "Column: '", firstNotIdentityField.Name, "'");
                result.AppendLine(2, "Filter:");
                foreach (var column in _columns)
                {
                    if (!column.IsIdentity)
                    {
                        result.AppendLine(3, column.Name, ": ''");
                    }
                }
            }
            result.AppendLine();
            result.AppendLine(1, "url: ->");
            result.AppendLine(2, "url = ", prefix, singleName, "ListingModel.ListingUrl;");
            result.AppendLine(2, "url += \"?page=\" + @attributes.Page");
            result.AppendLine(2, "url += \"&size=\" + @attributes.Size");
            result.AppendLine(2, "url += \"&Column=\" + @attributes.Column");
            result.AppendLine(2, "url += \"&Direction=\" + @attributes.Direction");
            foreach (var column in _columns)
            {
                if (!column.IsIdentity)
                {
                    result.AppendLine(2, "url += \"&Filter.", column.Name, "=\" + @attributes.Filter.", column.Name);
                }
            }
            result.AppendLine(2, "SiteMap.Admin.", name, ".Listing = encodeURI(url)");
            result.AppendLine(2, "return SiteMap.Admin.", name, ".Listing");
            result.AppendLine();
            result.AppendLine(1, "parse: (response) ->");
            result.AppendLine(2, "attrs = {");
            result.AppendLine(3, "Size: response.Size");
            result.AppendLine(3, "Page: response.Page");
            result.AppendLine(3, "TotalPages: response.TotalPages");
            result.AppendLine(3, "Column: response.Column");
            result.AppendLine(3, "Direction: response.Direction");
            result.AppendLine(3, "Filter: @attributes.Filter");
            result.AppendLine(2, "}");
            result.AppendLine();
            result.AppendLine(2, "attrs.", name, " = response.", name);
            foreach (var column in _columns)
            {
                if (!column.IsIdentity)
                {
                    result.AppendLine(2, "if response.Filter.", column.Name, "?");
                    result.AppendLine(3, "attrs.Filter.", column.Name, " = response.Filter.", column.Name);
                }
            }
            result.AppendLine();
            result.Append(2, "return attrs");

            return result.ToString();
        }

        public string GenerateCreateModel()
        {
            var result = new StringBuilder();

            result.AppendLine("class @", prefix, singleName, "CreateModel extends Backbone.Model");
            result.AppendLine();
            result.AppendLine(1, "defaults:");
            result.AppendLine(2, "SuccessMessage: ''");
            foreach (var column in _columns)
            {
                if (!column.IsIdentity)
                {
                    result.AppendLine(2, column.Name, ": ''");
                }
            }
            result.AppendLine();
            result.AppendLine(1, "url: ->");
            result.AppendLine(2, "url = SiteMap.Admin.", name, ".Create");
            result.Append(2, "return encodeURI(url)");

            return result.ToString();
        }

        public string GenerateEditModel()
        {
            var result = new StringBuilder();

            result.AppendLine("class @", prefix, singleName, "EditModel extends Backbone.Model");
            result.AppendLine();
            result.AppendLine(1, "defaults:");
            result.AppendLine(2, "SuccessMessage: ''");
            foreach (var column in _columns)
            {
                result.AppendLine(2, column.Name, ": ''");
            }
            result.AppendLine();
            result.AppendLine(1, "url: ->");
            result.AppendLine(2, "url = SiteMap.Admin.", name, ".Edit");
            var isAdded = false;
            foreach (var column in _columns)
            {
                if (column.IsIdentity)
                {
                    result.Append(2, "url += \"");
                    if (!isAdded)
                    {
                        isAdded = true;
                        result.Append("?");
                    }
                    else
                    {
                        result.Append("&");
                    }
                    result.AppendLine(column.Name.ToLowerFirstChar(), "=\" + @attributes.", column.Name);
                }
            }
            result.AppendLine(2, "return encodeURI(url)");
            result.AppendLine();
            result.AppendLine(1, "parse: (response) ->");
            result.AppendLine(2, "return {");
            result.AppendLine(3, "SuccessMessage: response.SuccessMessage");
            foreach (var column in _columns)
            {
                if (!column.IsIdentity)
                {
                    result.AppendLine(3, column.Name, ": response.", column.Name);
                }
            }
            result.Append(2, "}");

            return result.ToString();
        }

        public string GenerateEditView()
        {
            var result = new StringBuilder();

            result.AppendLine("class @", prefix, singleName, "EditView extends CustomView");
            result.AppendLine();
            result.AppendLine(1, "el: $('#content')");
            result.AppendLine();
            result.AppendLine(1, "initialize: ->");
            result.AppendLine(2, "super");
            result.AppendLine(2, "@model.bind(\"change\", @render)");
            result.AppendLine(2, "@model.fetch()");
            result.AppendLine();
            result.AppendLine(1, "render: ->");
            result.AppendLine(2, "@GetTemplate(SiteMap.Admin.", name, ".EditTemplate, (compiledTemplate) =>");
            result.AppendLine(3, "$(@el).html(compiledTemplate(@model.toJSON()))");
            result.AppendLine(2, ")");
            result.AppendLine();
            result.AppendLine(1, "events:");
            result.AppendLine(2, "\"click .buttons > button.save\": \"save\"");
            result.AppendLine(2, "\"click .buttons > button.cancel\": \"cancel\"");
            result.AppendLine();
            result.AppendLine(1, "save: (event) ->");
            result.AppendLine(2, "event.preventDefault()");
            result.AppendLine(2, "Loading.Start()");
            result.AppendLine(2, "Messages.WriteMessage('Saving ", singleName.ToLowerFirstChar(), ".')");
            result.AppendLine();
            result.AppendLine(2, "data = @$el.find(\"form.general\").serialize()");
            result.AppendLine();
            result.AppendLine(2, "$.post(@model.url(), data, (response) =>");
            result.AppendLine(3, "if (response.SuccessMessage? && response.SuccessMessage.length)");
            result.AppendLine(4, "Messages.WriteSuccessMessage(response.SuccessMessage)");
            result.AppendLine(4, "@Navigate(SiteMap.Admin.", name, ".Listing, true)");
            result.AppendLine(3, "else");
            result.AppendLine(4, "Messages.WriteErrors(response.Errors)");
            result.AppendLine(4, "Loading.Stop()");
            result.AppendLine(2, ")");
            result.AppendLine();
            result.AppendLine(1, "cancel: (event) ->");
            result.AppendLine(2, "event.preventDefault()");
            result.AppendLine(2, "Loading.Start()");
            result.AppendLine(2, "Messages.Clear()");
            result.Append(2, "@Navigate(SiteMap.Admin.", name, ".Listing, true)");

            return result.ToString();
        }

        public string GenerateCreateView()
        {
            var result = new StringBuilder();

            result.AppendLine("class @", prefix, singleName, "CreateView extends CustomView");
            result.AppendLine();
            result.AppendLine(1, "el: $('#content')");
            result.AppendLine();
            result.AppendLine(1, "initialize: ->");
            result.AppendLine(2, "super");
            result.AppendLine();
            result.AppendLine(1, "render: ->");
            result.AppendLine(2, "@GetTemplate(SiteMap.Admin.", name, ".CreateTemplate, (compiledTemplate) =>");
            result.AppendLine(3, "$(@el).html(compiledTemplate(@model.toJSON()))");
            result.AppendLine(2, ")");
            result.AppendLine();
            result.AppendLine(1, "events:");
            result.AppendLine(2, "\"click .buttons > button.save\": \"save\"");
            result.AppendLine(2, "\"click .buttons > button.cancel\": \"cancel\"");
            result.AppendLine();
            result.AppendLine(1, "save: (event) ->");
            result.AppendLine(2, "event.preventDefault()");
            result.AppendLine(2, "Loading.Start()");
            result.AppendLine(2, "Messages.WriteMessage('Creating ", singleName.ToLowerFirstChar(), ".')");
            result.AppendLine();
            result.AppendLine(2, "data = $(@el).find('form.general').serialize()");
            result.AppendLine();
            result.AppendLine(2, "$.post(@model.url(), date, (response) =>");
            result.AppendLine(3, "if (response.SuccessMessage? && response.SuccessMessage.length)");
            result.AppendLine(4, "Messages.WriteSuccessMessage(response.SuccessMessage)");
            result.AppendLine(4, "@Navigate(SiteMap.Admin.", name, ".Listing, true)");
            result.AppendLine(3, "else");
            result.AppendLine(4, "Messages.WriteErrors(response.Errors)");
            result.AppendLine(4, "Loading.Stop()");
            result.AppendLine(2, ")");
            result.AppendLine();
            result.AppendLine(1, "cancel: (event) ->");
            result.AppendLine(2, "event.preventDefault()");
            result.AppendLine(2, "Loading.Start()");
            result.AppendLine(2, "Messages.Clear()");
            result.Append(2, "@Navigate(SiteMap.Admin.", name, ".Listing, true)");

            return result.ToString();
        }

        public string GenerateListingView()
        {
            var result = new StringBuilder();

            result.AppendLine("class @", prefix, singleName, "ListingView extends ListingView");
            result.AppendLine();
            result.AppendLine(1, "el: $('#content')");
            result.AppendLine();
            result.AppendLine(1, "initialize: ->");
            result.AppendLine(2, "super");
            result.AppendLine(2, "@model.bind(\"change\", @render)");
            result.AppendLine(2, "@model.fetch()");
            result.AppendLine();
            result.AppendLine(1, "render: ->");
            result.AppendLine(2, "@Navigate(@model.url(), false, true)");
            result.AppendLine(2, "@GetTemplate(SiteMap.Admin.", name, ".ListingTemplate, (compiledTemplate) =>");
            result.AppendLine(3, "$(@el).html(compiledTemplate(@model.toJSON()))");
            result.AppendLine(2, ")");
            result.AppendLine();
            result.AppendLine(1, "events:");
            result.AppendLine(2, "\"click .search\": \"search\"");
            result.AppendLine(2, "\"click .pagination a\": \"pagination\"");
            result.AppendLine(2, "\"click th > a\": \"sort\"");
            if (_isCreate)
                result.AppendLine(2, "\"click .buttons > .create\": \"create\"");
            if (_isDelete)
                result.AppendLine(2, "\"click .buttons > .delete\": \"remove\"");
            if (_isSave)
                result.AppendLine(2, "\"click tbody tr>td:not(.chk)\": \"edit\"");
            result.AppendLine(2, "\"change .page-size\": \"changeSize\"");
            result.AppendLine(2, "\"click .reset\": \"reset\"");
            result.AppendLine();
            result.AppendLine(1, "search: (event) ->");
            result.AppendLine(2, "event.preventDefault()");
            result.AppendLine();
            result.AppendLine(2, "### --- Change if you need. --- ###");
            foreach (var column in _columns)
            {
                if (!column.IsIdentity)
                {
                    result.AppendLine(2, column.Name.ToLowerFirstChar(), " = $('#Filter_", column.Name, "').val()");
                }
            }
            result.AppendLine(2, "filter = @model.get(\"Filter\")");
            foreach (var column in _columns)
            {
                if (!column.IsIdentity)
                {
                    result.AppendLine(2, "filter.", column.Name, " = ", column.Name.ToLowerFirstChar());
                }
            }
            result.AppendLine(2, "### --------------------------- ###");
            result.AppendLine();
            result.AppendLine(2, "Messages.WriteMessage('Apply search.')");
            result.AppendLine(2, "Loading.Start()");
            result.AppendLine(2, "@model.set(\"Filter\", filter, silent: true)");
            result.AppendLine(2, "@model.set(\"Page\", 1, silent: true)");
            result.AppendLine(2, "@model.fetch()");
            result.AppendLine();
            result.AppendLine(1, "reset: (event) ->");
            result.AppendLine(2, "Loading.Start()");
            result.AppendLine(2, "@model.set(");
            result.AppendLine(3, "\"Filter\"");
            result.AppendLine(3, "{");
            foreach (var column in _columns)
            {
                if (!column.IsIdentity)
                {
                    result.AppendLine(4, column.Name, ": ", GetDefaultJSValue(GetCSharpType(column.DataTypeName, column.IsNullable)));
                }
            }
            result.AppendLine(3, "}");
            result.AppendLine(3, "silent: true");
            result.AppendLine(2, ")");
            result.AppendLine(2, "@model.set(\"Page\", 1, silent: true)");
            result.AppendLine(2, "@model.fetch()");

            if (_isCreate)
            {
                result.AppendLine();
                result.AppendLine(1, "create: (event) ->");
                result.AppendLine(2, "event.preventDefault()");
                result.AppendLine(2, "Loading.Start()");
                result.AppendLine(2, "@Navigate(SiteMap.Admin.", name, ".Create, true)");
            }

            if (_isDelete)
            {
                result.AppendLine();
                result.AppendLine(1, "remove: (event) ->");
                result.AppendLine(2, "event.preventDefault()");
                result.AppendLine(2, "Messages.Clear()");
                result.AppendLine(2, "form = $(@el).find(\"form.listing\")");
                result.AppendLine(2, "if form.find(\"input:checked\").length");
                result.AppendLine(3, "confirmDialog = new ", singleName, "DeleteConfirm(@)");
                result.AppendLine(3, "confirmDialog.Show(\"Are you sure?\")");
                result.AppendLine(2, "else");
                result.AppendLine(3, "Messages.WriteMessage('Choose one or more ", name.ToLowerFirstChar(), ".')");
                result.AppendLine();
                result.AppendLine("class ", singleName, "DeleteConfirm extends ConfirmDialog");
                result.AppendLine(1, "constructor: (view) ->");
                result.AppendLine(2, "@view = view");
                result.AppendLine(1, "Ok: ->");
                result.AppendLine(2, "form = $(@view.el).find(\"form.listing\")");
                result.AppendLine(2, "data = form.serialize()");
                result.AppendLine(2, "Loading.Start()");
                result.AppendLine(2, "Messages.WriteMessage('Deleting ", name.ToLowerFirstChar(), ".')");
                result.AppendLine(2, "$.post(SiteMap.Admin.", name, ".Delete, data, (response) =>");
                result.AppendLine(3, "if (response.SuccessMessage? && response.SuccessMessage.length)");
                result.AppendLine(4, "Messages.WriteSuccessMessage(response.SuccessMessage)");
                result.AppendLine(4, "@view.model.fetch()");
                result.AppendLine(3, "else ");
                result.AppendLine(4, "Messages.WriteErrors(response.Errors)");
                result.AppendLine(4, "Loading.Stop()");
                result.Append(2, ")");
            }

            return result.ToString();
        }

        public string GenerateRoutes()
        {
            var result = new StringBuilder();

            result.AppendLine("class @", prefix, singleName, "Routes extends Backbone.Router");
            result.AppendLine();
            result.AppendLine(1, "routes:");
            if (_isCreate)
            {
                result.AppendLine(2, "\"", name, "/Create\": \"Create\"");
            }
            if (_isSave)
            {
                result.AppendLine(2, "\"", name, "/Edit*path\": \"Edit\"");
            }
            if (_isListing)
            {
                result.AppendLine(2, "\"", name, "/Listing*path\": \"Listing\"");
                result.AppendLine(2, "\"", name, "/Index\": \"Listing\"");
                result.AppendLine(2, "\"", name, "\": \"Listing\"");
            }

            if (_isCreate)
            {
                result.AppendLine();
                result.AppendLine(1, "Create: ->");
                result.AppendLine(2, "model = new ", prefix, singleName, "CreateModel()");
                result.AppendLine(2, "view = new ", prefix, singleName, "CreateView(model: model)");
                result.AppendLine(2, "view.render()");
            }
            if (_isSave)
            {
                result.AppendLine();
                result.AppendLine(1, "Edit: ->");
                result.AppendLine(2, "model = new ", prefix, singleName, "EditModel()");
                foreach (var column in _columns)
                {
                    if (column.IsIdentity)
                    {
                        result.AppendLine(2, "model.set(\"", column.Name, "\", Url.GetParameterByName(\"", column.Name, "\"))");
                    }
                }
                result.AppendLine(2, "view = new ", prefix, singleName, "EditView(model: model)");
            }
            if (_isListing)
            {
                result.AppendLine();
                result.AppendLine(1, "Listing: ->");
                result.AppendLine(2, "model = new ", prefix, singleName, "ListingModel()");
                result.AppendLine(2, "model.FillModelFromUrl()");
                result.AppendLine(2, "view = new ", prefix, singleName, "ListingView(model: model)");
            }

            return result.ToString();
        }

        private static string GetDefaultJSValue(string cSharpType)
        {
            if (cSharpType.Last() == '?')
                return "''";

            switch(cSharpType)
            {
                case "int":
                case "double":
                case "byte":
                    return "0";
                case "string":
                    return "''";
                default:
                    return "''";
            }
        }

        private static string GetCSharpType(string sqlType, bool isNullable)
        {
            if (sqlType.StartsWith("nvarchar") || sqlType.StartsWith("string"))
            {
                return "string";
            }

            switch (sqlType)
            {
                case "int":
                    return isNullable ? "int?" : "int";
                case "float":
                    return isNullable ? "double?" : "double";
                case "tinyint":
                    return isNullable ? "byte?" : "byte";
                case "date":
                case "datetime":
                case "datetime2":
                    return isNullable ? "System.DateTime?" : "System.DateTime";
                case "binary":
                    return "object";
                default:
                    return "dynamic";
            }
        }
    }
}